// Memory. This memory design uses 8 memory banks, each 
// of which is one byte wide. Banking allows us to select an 
// arbitrary set of 6 contiguous bytes for instruction reading 
// and an arbitrary set of 4 contiguous bytes 
// for data reading & writing. 
// It uses an external RAM module from either the file 
// combram.v (using combinational reads) 
// or synchram.v (using clocked reads) 
// The SEQ & SEQ+ processors only work with combram.v. 
// PIPE works with either. 
module bmemory(maddr, wenable, wdata, renable, rdata, m_ok, 
    iaddr, instr, i_ok, clock); 
parameter memsize = 4096; // Number of bytes in memory 

input [31:0] maddr; // Read/Write address 
input wenable; // Write enable 
input [31:0] wdata; // Write data 
input renable; // Read enable 
output [31:0] rdata; // Read data 
output m_ok; // Read & write addresses within range 
input [31:0] iaddr; // Instruction address 
output [47:0] instr; // 6 bytes of instruction 
output i_ok; // Instruction address within range 
input clock; 

wire [7:0] ib0, ib1, ib2, ib3, ib4, ib5; // Instruction bytes 
wire [7:0] db0, db1, db2, db3; // Data bytes 
wire [2:0] ibid = iaddr[2:0]; // Instruction Bank ID 
wire [28:0] iindex = iaddr[31:3]; // Address within bank 
wire [28:0] iip1 = iindex+1; // Next address within bank 
wire [2:0] mbid = maddr[2:0]; // Data Bank ID 
wire [28:0] mindex = maddr[31:3]; // Address within bank 
wire [28:0] mip1 = mindex+1; // Next address within bank 
// Instruction addresses for each bank 
wire [28:0] addrI0, addrI1, addrI2, addrI3, addrI4, addrI5, addrI6, addrI7; 
// Instruction data for each bank 
wire [7:0] outI0, outI1, outI2, outI3, outI4, outI5, outI6, outI7; 
// Data addresses for each bank 
wire [28:0] addrD0, addrD1, addrD2, addrD3, addrD4, addrD5, addrD6, addrD7; 
// Data output for each bank 
wire [7:0] outD0, outD1, outD2, outD3, outD4, outD5, outD6, outD7; 
// Data input for each bank 
wire [7:0] inD0, inD1, inD2, inD3, inD4, inD5, inD6, inD7; 
// Data write enable signals for each bank 
wire dwEn0, dwEn1, dwEn2, dwEn3, dwEn4, dwEn5, dwEn6, dwEn7; 
// The bank memories 
combram #(8, memsize/8, 29, "bank0.data") bank0(clock, addrI0, 1'b0, 8'h0, 1'b1, outI0, // Instruction 
    addrD0, dwEn0, inD0, renable, outD0); // Data 
combram #(8, memsize/8, 29, "bank1.data") bank1(clock, addrI1, 1'b0, 8'h0, 1'b1, outI1, // Instruction 
    addrD1, dwEn1, inD1, renable, outD1); // Data 
combram #(8, memsize/8, 29, "bank2.data") bank2(clock, addrI2, 1'b0, 8'h0, 1'b1, outI2, // Instruction 
    addrD2, dwEn2, inD2, renable, outD2); // Data 
combram #(8, memsize/8, 29, "bank3.data") bank3(clock, addrI3, 1'b0, 8'h0, 1'b1, outI3, // Instruction 
    addrD3, dwEn3, inD3, renable, outD3); // Data 
combram #(8, memsize/8, 29, "bank4.data") bank4(clock, addrI4, 1'b0, 8'h0, 1'b1, outI4, // Instruction 
    addrD4, dwEn4, inD4, renable, outD4); // Data 
combram #(8, memsize/8, 29, "bank5.data") bank5(clock, addrI5, 1'b0, 8'h0, 1'b1, outI5, // Instruction 
    addrD5, dwEn5, inD5, renable, outD5); // Data 
combram #(8, memsize/8, 29, "bank6.data") bank6(clock, addrI6, 1'b0, 8'h0, 1'b1, outI6, // Instruction 
    addrD6, dwEn6, inD6, renable, outD6); // Data 
combram #(8, memsize/8, 29, "bank7.data") bank7(clock, addrI7, 1'b0, 8'h0, 1'b1, outI7, // Instruction 
    addrD7, dwEn7, inD7, renable, outD7); // Data 

// Determine the instruction addresses for the banks 
assign addrI0 = ibid >= 3 ? iip1 : iindex; 
assign addrI1 = ibid >= 4 ? iip1 : iindex; 
assign addrI2 = ibid >= 5 ? iip1 : iindex; 
assign addrI3 = ibid >= 6 ? iip1 : iindex; 
assign addrI4 = ibid >= 7 ? iip1 : iindex; 
assign addrI5 = iindex; 
assign addrI6 = iindex; 
assign addrI7 = iindex; 
// Get the bytes of the instruction 
assign i_ok = 
    (iaddr + 5) < memsize; 
assign ib0 = !i_ok ? 0 : 
    ibid == 0 ? outI0 : 
    ibid == 1 ? outI1 : 
    ibid == 2 ? outI2 : 
    ibid == 3 ? outI3 : 
    ibid == 4 ? outI4 : 
    ibid == 5 ? outI5 : 
    ibid == 6 ? outI6 : 
    outI7; 
assign ib1 = !i_ok ? 0 : 
    ibid == 0 ? outI1 : 
    ibid == 1 ? outI2 : 
    ibid == 2 ? outI3 : 
    ibid == 3 ? outI4 : 
    ibid == 4 ? outI5 : 
    ibid == 5 ? outI6 : 
    ibid == 6 ? outI7 : 
    outI0; 
assign ib2 = !i_ok ? 0 : 
    ibid == 0 ? outI2 : 
    ibid == 1 ? outI3 : 
    ibid == 2 ? outI4 : 
    ibid == 3 ? outI5 : 
    ibid == 4 ? outI6 : 
    ibid == 5 ? outI7 : 
    ibid == 6 ? outI0 : 
    outI1; 
assign ib3 = !i_ok ? 0 : 
    ibid == 0 ? outI3 : 
    ibid == 1 ? outI4 : 
    ibid == 2 ? outI5 : 

    ibid == 3 ? outI6 : 
    ibid == 4 ? outI7 : 
    ibid == 5 ? outI0 : 
    ibid == 6 ? outI1 : 
    outI2; 
assign ib4 = !i_ok ? 0 : 
    ibid == 0 ? outI4 : 
    ibid == 1 ? outI5 : 
    ibid == 2 ? outI6 : 
    ibid == 3 ? outI7 : 
    ibid == 4 ? outI0 : 
    ibid == 5 ? outI1 : 
    ibid == 6 ? outI2 : 
    outI3; 
assign ib5 = !i_ok ? 0 : 
    ibid == 0 ? outI5 : 
    ibid == 1 ? outI6 : 
    ibid == 2 ? outI7 : 
    ibid == 3 ? outI0 : 
    ibid == 4 ? outI1 : 
    ibid == 5 ? outI2 : 
    ibid == 6 ? outI3 : 
    outI4; 
assign instr[ 7: 0] = ib0; 
assign instr[15: 8] = ib1; 
assign instr[23:16] = ib2; 
assign instr[31:24] = ib3; 
assign instr[39:32] = ib4; 
assign instr[47:40] = ib5;

assign m_ok = 
    (!renable & !wenable | (maddr + 3) < memsize); 
assign addrD0 = mbid >= 5 ? mip1 : mindex; 
assign addrD1 = mbid >= 6 ? mip1 : mindex; 
assign addrD2 = mbid >= 7 ? mip1 : mindex; 
assign addrD3 = mindex; 
assign addrD4 = mindex; 
assign addrD5 = mindex; 
assign addrD6 = mindex; 
assign addrD7 = mindex; 
// Get the bytes of data; 
assign db0 = !m_ok ? 0 : 
    mbid == 0 ? outD0 : 
    mbid == 1 ? outD1 : 
    mbid == 2 ? outD2 : 
    mbid == 3 ? outD3 : 
    mbid == 4 ? outD4 : 
    mbid == 5 ? outD5 : 
    mbid == 6 ? outD6 : 
    outD7; 
assign db1 = !m_ok ? 0 : 
    mbid == 0 ? outD1 : 
    mbid == 1 ? outD2 : 
    mbid == 2 ? outD3 : 
    mbid == 3 ? outD4 : 
    mbid == 4 ? outD5 : 
    mbid == 5 ? outD6 : 
    mbid == 6 ? outD7 : 
    outD0; 
assign db2 = !m_ok ? 0 : 
    mbid == 0 ? outD2 : 
    mbid == 1 ? outD3 : 
    mbid == 2 ? outD4 : 
    mbid == 3 ? outD5 : 
    mbid == 4 ? outD6 : 
    mbid == 5 ? outD7 : 
    mbid == 6 ? outD0 : 
    outD1; 
assign db3 = !m_ok ? 0 : 
    mbid == 0 ? outD3 : 
    mbid == 1 ? outD4 : 
    mbid == 2 ? outD5 : 
    mbid == 3 ? outD6 : 
    mbid == 4 ? outD7 : 
    mbid == 5 ? outD0 : 
    mbid == 6 ? outD1 : 
    outD2; 
assign rdata[ 7: 0] = db0; 
assign rdata[15: 8] = db1; 
assign rdata[23:16] = db2; 
assign rdata[31:24] = db3; 
wire [7:0] wd0 = wdata[7:0]; 
wire [7:0] wd1 = wdata[15:8]; 
wire [7:0] wd2 = wdata[23:16]; 
wire [7:0] wd3 = wdata[31:24]; 
assign inD0 = 
    mbid==5?wd3: 
    mbid==6?wd2: 
    mbid==7?wd1: 
    mbid==0?wd0: 
    0; 
assign inD1 = 
    mbid==6?wd3: 
    mbid==7?wd2: 
    mbid==0?wd1: 
    mbid==1?wd0: 
    0; 
assign inD2 = 
    mbid==7?wd3: 
    mbid==0?wd2: 
    mbid==1?wd1: 
    mbid==2?wd0: 
    0; 
assign inD3 = 
    mbid==0?wd3: 
    mbid==1?wd2: 
    mbid==2?wd1: 
    mbid==3?wd0: 
    0; 
assign inD4 = 
    mbid==1?wd3: 
    mbid==2?wd2: 
    mbid==3?wd1: 
    mbid==4?wd0: 
    0; 
assign inD5 = 
    mbid==2?wd3: 
    mbid==3?wd2: 
    mbid==4?wd1: 
    mbid==5?wd0: 
    0; 
assign inD6 = 
    mbid==3?wd3: 
    mbid==4?wd2: 
    mbid==5?wd1: 
    mbid==6?wd0: 
    0; 
assign inD7 = 
    mbid==4?wd3: 
    mbid==5?wd2: 
    mbid==6?wd1: 
    mbid==7?wd0: 
    0; 
// Which banks get written 
assign dwEn0 = wenable & (mbid <= 0 | mbid >= 5); 
assign dwEn1 = wenable & (mbid <= 1 | mbid >= 6); 
assign dwEn2 = wenable & (mbid <= 2 | mbid >= 7); 
assign dwEn3 = wenable & (mbid <= 3); 
assign dwEn4 = wenable & (mbid >= 1 & mbid <= 4); 
assign dwEn5 = wenable & (mbid >= 2 & mbid <= 5); 
assign dwEn6 = wenable & (mbid >= 3 & mbid <= 6); 
assign dwEn7 = wenable & (mbid >= 4); 
endmodule

